package com.guahao.portal.swagger.dubbo.http;

import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.annotation.Service;
import org.apache.dubbo.config.spring.ServiceBean;
import org.apache.dubbo.config.spring.extension.SpringExtensionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.context.ApplicationContext;

public class ReferenceManager {
    private static Logger LOGGER = LoggerFactory.getLogger(ReferenceManager.class);

    @SuppressWarnings("rawtypes")
    private static Collection<ServiceBean> services;

    private static Map<Class<?>, Object> interfaceMapProxy = new ConcurrentHashMap<Class<?>, Object>();
    private static Map<Class<?>, Object> interfaceMapRef = new ConcurrentHashMap<Class<?>, Object>();

    private static ReferenceManager instance;
    private static ApplicationConfig application;

    private ReferenceManager() {
    }

    @SuppressWarnings("rawtypes")
    public synchronized static ReferenceManager getInstance() {
        if (null != instance) {
            return instance;
        }

        instance = new ReferenceManager();

        try {
            ApplicationContext context = getApplicationContext();

            // 使用XML方式定义的dubbo服务，如 <dubbo:service interface="" ref="" />
            Map<String, ServiceBean> xmlBeans = context.getBeansOfType(ServiceBean.class);
            services = xmlBeans.values();
            for (ServiceBean<?> bean : services) {
                interfaceMapRef.put(bean.getInterfaceClass(), bean.getRef());
            }

            // 使用注解方式定义的dubbo服务，如 @org.apache.dubbo.config.annotation.Service
            Map<String, Object> annotationBeans = context.getBeansWithAnnotation(Service.class);
            for (Object bean : annotationBeans.values()) {
                // 使用 AopProxyUtils 获取一个代理对象的最终对象类型
                Class<?> clz = AopProxyUtils.ultimateTargetClass(bean);

                // 获取 service 对象的实现接口类
                Class<?>[] intfs = clz.getInterfaces();
                for (Class<?> intf : intfs) {
                    interfaceMapRef.put(intf, bean);
                }
            }
        } catch (Exception e) {
            LOGGER.error("=======> Get All Dubbo Service Error", e);
            return instance;
        }

        if (!services.isEmpty()) {
            ServiceBean<?> bean = services.toArray(new ServiceBean[]{})[0];
            application = bean.getApplication();
        }

        return instance;
    }

    public Object getProxy(String interfaceClass) {
        Set<Entry<Class<?>, Object>> entrySet = interfaceMapProxy.entrySet();
        for (Entry<Class<?>, Object> entry : entrySet) {
            if (entry.getKey().getName().equals(interfaceClass)) {
                return entry.getValue();
            }
        }

        for (ServiceBean<?> service : services) {
            if (interfaceClass.equals(service.getInterfaceClass().getName())) {
                ReferenceConfig<Object> reference = new ReferenceConfig<Object>();
                reference.setApplication(service.getApplication());
                reference.setRegistry(service.getRegistry());
                reference.setRegistries(service.getRegistries());
                reference.setInterface(service.getInterfaceClass());
                reference.setVersion(service.getVersion());
                interfaceMapProxy.put(service.getInterfaceClass(), reference.get());
                return reference.get();
            }
        }

        // 适用于注解方式定义的dubbo服务，如 @com.alibaba.dubbo.config.annotation.Service
        try {
            Map<String, ?> beansOfType = getApplicationContext().getBeansOfType(Class.forName(interfaceClass));
            return beansOfType.values().toArray()[0];
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        return null;
    }

    public Entry<Class<?>, Object> getRef(String interfaceClass) {
        Set<Entry<Class<?>, Object>> entrySet = interfaceMapRef.entrySet();
        for (Entry<Class<?>, Object> entry : entrySet) {
            if (entry.getKey().getName().equals(interfaceClass)) {
                return entry;
            }
        }
        return null;
    }

    @SuppressWarnings("rawtypes")
    public Collection<ServiceBean> getServices() {
        return services;
    }

    public ApplicationConfig getApplication() {
        return application;
    }

    public Map<Class<?>, Object> getInterfaceMapRef() {
        return interfaceMapRef;
    }

    private static ApplicationContext getApplicationContext() {
        ApplicationContext context = null;

        for (ApplicationContext c : SpringExtensionFactory.getContexts()) {
            if (c != null) {
                context = c;
                break;
            }
        }

        return context;
    }

}
